
#include    <stdio.h>
#include    <string.h>
#include    <stdarg.h>

#include    <vector>


#include    "tiny/logger.hpp"
#include    "tiny/consoleappender.hpp"

using namespace std;
using namespace tiny;


const   std::string     Logger::CATEGORY_ROOT("root");
const   std::string     Logger::CATEGORY_CONSOLE("console");

Logger::category_map_type       Logger::m_categories;
Logger::appender_map_type       Logger::m_appenders;

///////////////////////////////////////////////////////////////////////

Logger::Logger(const std::string& strCategory)
    :   m_strCategory(strCategory)
{
    m_pszMessageBuf = new char[SIZE_MESSAGE_BUF];
    m_pszTimeStampBuf = new char[SIZE_TIMESTAMP_BUF];
}

Logger::~Logger()
{
    loglevel_appenders_type::iterator    it = m_loglevel_appenders.begin();
    while( m_loglevel_appenders.end() != it ) {
        appender_map_type& appenders = it->second;
        appender_map_type::iterator    _it = appenders.begin();
        while( appenders.end() != _it ) {
            LogAppender*& pAppender = _it->second;
            delete pAppender;

            ++_it;
        }

        ++it;
    }

    delete [] m_pszMessageBuf;
    delete [] m_pszTimeStampBuf;
}

///////////////////////////////////////////////////////////////////////

Logger*
Logger::getInstance(const char* pszCategory)
{
    category_map_type::iterator  result = m_categories.find(pszCategory);
    if ( m_categories.end() != result ) {
        return  result->second;
    }

    result = m_categories.find(CATEGORY_ROOT);
    if ( m_categories.end() != result ) {
        return  result->second;
    }

    Logger* pLogger = new Logger(CATEGORY_ROOT);
    m_categories[CATEGORY_ROOT] = pLogger;
    
    return  pLogger;
}


Logger*
tiny::getLogger(const char* pszCategory)
{
    return  Logger::getInstance(pszCategory);
}

void
Logger::configure(const char* pszConfigure)
{
    if (NULL == pszConfigure) {
        return;
    }
    if ((0 == strlen(pszConfigure)) || 
        (0 == strcasecmp(Logger::CATEGORY_CONSOLE.c_str(), pszConfigure)) || 
        (0 == strcasecmp(Logger::CATEGORY_ROOT.c_str(), pszConfigure))) {
        // configure console-appender only
        if (m_appenders.size() > 0) {
            // if there is any appender loaded, no console-appender be loaded again.
            return;
        }

        LogAppender*    pAppender = new ConsoleAppender();
        m_appenders[ Logger::CATEGORY_CONSOLE ] = pAppender;
        
        Logger* pLogger = new Logger(Logger::CATEGORY_ROOT);
        
        // set appender to logger
        std::vector<int>    loglevels;
        loglevels.push_back( LogLevel::FATAL );
        loglevels.push_back( LogLevel::ERROR );
        loglevels.push_back( LogLevel::WARN );
        loglevels.push_back( LogLevel::INFO );
        loglevels.push_back( LogLevel::DEBUG );
        loglevels.push_back( LogLevel::TRACE );

        int     iMinLogLevel = pAppender->getMinLogLevel();
        int     iMaxLogLevel = pAppender->getMaxLogLevel();
        std::vector<int>::iterator  it = loglevels.begin();
        while ( loglevels.end() != it ) {
            if ((*it >= iMinLogLevel) && (*it <= iMaxLogLevel)) {
                pLogger->insertLogLevelAppender(*it, Logger::CATEGORY_CONSOLE, pAppender );
            }

            ++it;
        }

        m_categories[CATEGORY_ROOT] = pLogger;
    }

}

///////////////////////////////////////////////////////////////////////

int
Logger::fatal(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::FATAL);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::FATAL, m_pszMessageBuf);
}

int
Logger::error(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::ERROR);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::ERROR, m_pszMessageBuf);
}

int
Logger::warn(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::WARN);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::WARN, m_pszMessageBuf);
}

int
Logger::info(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::INFO);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::INFO, m_pszMessageBuf);
}

int
Logger::debug(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::DEBUG);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::DEBUG, m_pszMessageBuf);
}

int
Logger::trace(const char* pszFileName, int iLineNo, const char* pszFormat, ...)
{
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(LogLevel::TRACE);
    if ( m_loglevel_appenders.end() == result ) {
        return  0;
    }
    appender_map_type  appenders = result->second;

    va_list args;
    va_start(args, pszFormat);
    int iLength = vsnprintf(m_pszMessageBuf, SIZE_MESSAGE_BUF - 1, pszFormat, args );
    m_pszMessageBuf[ iLength ] = '\0';
    va_end(args);

    return  append(appenders, pszFileName, iLineNo, LogLevel::TRACE, m_pszMessageBuf);
}


int     
Logger::append(appender_map_type& appenders, const char* pszFileName, int iLineNo, int iLevel, const char* pszMessage )
{
    const char* pszTimeStamp = getTimeStampString();
    const char* pszCategory = getCategory().c_str();

    LogRecord   record(pszTimeStamp, pszFileName, iLineNo, pszCategory, iLevel, pszMessage);

    appender_map_type::iterator    it = appenders.begin();
    while( appenders.end() != it ) {
        LogAppender*    pAppender = it->second;
        if ( NULL != pAppender ) {
            pAppender->append( record );
        }

        ++it;
    }

    return  1;
}


void    
Logger::insertLogLevelAppender(loglevel_type level, appender_name_type appender_name, LogAppender* pAppender)
{
    if ( NULL == pAppender ) {
        return;
    }
    
    loglevel_appenders_type::iterator    result = m_loglevel_appenders.find(level);
    if ( m_loglevel_appenders.end() == result ) {
        appender_map_type    _appenders;
        m_loglevel_appenders[ level ] = _appenders;
        
        result = m_loglevel_appenders.find(level);
    }
    appender_map_type&    map_appender = result->second;
    
    appender_map_type::iterator    result_appender = map_appender.find( appender_name );
    if (map_appender.end() == result_appender) {
        map_appender[ appender_name ] = pAppender;
    }
}

void    
Logger::eraseLogLevelAppender(loglevel_type level, appender_name_type appender_name )
{
}


///////////////////////////////////////////////////////////////////////

const char*
Logger::getTimeStampString()
{
    struct tm*  tmp;
    time_t  t;
    int     iReturn;

    t = time (NULL);
    tmp = localtime( &t );
    if ( NULL == tmp ) {
        return  NULL;
    }

    m_pszTimeStampBuf[0] = '\0';
    iReturn = strftime(m_pszTimeStampBuf, SIZE_TIMESTAMP_BUF, "%Y-%m-%d %H:%M:%S", tmp);
    if (iReturn > 0) {
        m_pszTimeStampBuf[ iReturn ] = '\0';
    }
    
    return  m_pszTimeStampBuf;
}

const std::string&
Logger::getCategory() const
{
    return  m_strCategory;
}

